#include <stdlib.h>
#include <string.h>
#include "crypto/types.h"
#include "crypto/tool.h"
#include "crypto/md5tool.h"

SAFE_array *preDeal(byte *data, int len)
{
    uint dataLength = len;
    uint blockNumber = dataLength / MD5_BLOCK_SIZE;
    uint modNumber = dataLength % MD5_BLOCK_SIZE;

    SAFE_array *sdataPtr;
    if (modNumber != MD5_FILLED_MOD)
    {
        int size;
        byte *visitPtr, *endPtr;
        uint appendLengthData;

        if (modNumber > MD5_FILLED_MOD)
        {
            blockNumber += 1;
        }
        blockNumber += 1;

        size = blockNumber * MD5_BLOCK_SIZE;
        sdataPtr = (SAFE_array *)malloc(sizeof(SAFE_array) + (size + 1) * sizeof(byte));
        sdataPtr->cap = size + 1;
        sdataPtr->size = size;
        memcpy(sdataPtr->data, data, len);
        visitPtr = sdataPtr->data + dataLength;
        endPtr = sdataPtr->data + (blockNumber - 1) * MD5_BLOCK_SIZE + MD5_FILLED_MOD;
        *visitPtr++ = 0x80;
        while (visitPtr != endPtr)
        {
            *visitPtr++ = 0x00;
        }
        // 只支持32字节长度的数据
        uint64 beDataLength = (uint64)(dataLength * 8);
        *(uint64 *)endPtr = beDataLength;
        *(sdataPtr->data + sdataPtr->size) = 0;
    }
    else
    {
        uint size = (blockNumber + 1) * MD5_BLOCK_SIZE;
        sdataPtr = (SAFE_array *)malloc(sizeof(SAFE_array) + (size + 1) * sizeof(byte));
        sdataPtr->cap = size + 1;
        sdataPtr->size = size;
        byte *endPtr = sdataPtr->data + dataLength;
        uint64 beDataLength = (uint64)(dataLength * 8);
        *(uint64 *)endPtr = beDataLength;
        *(sdataPtr->data + sdataPtr->size) = 0;
    }
    return sdataPtr;
}

static uint F(uint x, uint y, uint z)
{
    return (x & y) | ((~x) & z);
}

static uint G(uint x, uint y, uint z)
{
    return (x & z) | (y & (~z));
}

static uint H(uint x, uint y, uint z)
{
    return x ^ y ^ z;
}

static uint I(uint x, uint y, uint z)
{
    return y ^ (x | (~z));
}

static uint DD(uint a, uint b, uint c, uint d, uint Mi, uint16 s, uint j, operate opt)
{
    uint tmp = a + (opt(b, c, d) + Mi + tj[j]);
    ROL(&tmp, s);
    return b + tmp;
}

void hexLoop(int loopIndex, uint *md5Value, int *data)
{
    uint j, tmp;
    uint *a, *b, *c, *d;
    operate operators[4] = {F, G, H, I};

    j = loopIndex * 16;

    for (int i = 0; i < 16; i++)
    {
        a = md5Value + ((0 - i + 16) % 4);
        b = md5Value + ((1 - i + 16) % 4);
        c = md5Value + ((2 - i + 16) % 4);
        d = md5Value + ((3 - i + 16) % 4);
        *a = DD(*a, *b, *c, *d, data[mN[loopIndex][i]], rolarray[loopIndex][i % 4], j, operators[loopIndex]);
        j++;
    }
    return;
}